﻿package idv.cjcat.monica.platformer.player {
	import flash.display.DisplayObjectContainer;
	import flash.display.MovieClip;
	import idv.cjcat.monica.box2d.Box2DCategory;
	import idv.cjcat.monica.Const;
	import idv.cjcat.monica.Ref;
	import idv.cjcat.rusher.box2d.Box2DBodyComponent;
	import idv.cjcat.rusher.box2d.events.ContactEvent;
	import idv.cjcat.rusher.engine.components.ActiveComponent;
	import idv.cjcat.rusher.engine.logger.LogType;
	import idv.cjcat.rusher.engine.ui.Key;
	import idv.cjcat.rusher.engine.ui.KeyManager;
	import idv.cjcat.rusher.events.KeyManagerEvent;
	import idv.cjcat.rusher.geom.Vec2D;
	import idv.cjcat.rusher.math.RusherMath;
	
	public class PlayerController extends ActiveComponent {
		
		internal static const MAX_JUMPS:int = 2;
		
		internal static const GROUND_PUSHING_FORCE:Number = 0;
		
		public static const WALKING_SPEED:Number = 400;
		public static const RUNNING_SPEED:Number = 660;
		internal static const JUMP_BOOST:Number = 1100;
		internal static const JUMP_CANCEL_FACTOR:Number = 0.5;
		internal static const REBOUNDING_MOVING_SPEED:Number = 350;
		internal static const REBOUNDING_MOVING_SPEED_OFFSET:Number = 0;
		internal static const MAX_FALLING_SPEED:Number = JUMP_BOOST;
		
		internal static const FRONT_CAMERA_TARGET_OFFSET_WALKING:Vec2D = new Vec2D(800, 0);
		internal static const FRONT_CAMERA_TARGET_OFFSET_RUNNING:Vec2D = new Vec2D(1000, 0);
		internal static const FRONT_CAMERA_TARGET_OFFSET_STANDING:Vec2D = new Vec2D(400, 0);
		internal static const BACK_CAMERA_TARGET_OFFSET_WALKING:Vec2D = new Vec2D(-400, 0);
		internal static const BACK_CAMERA_TARGET_OFFSET_RUNNING:Vec2D = new Vec2D(-600, 0);
		internal static const BACK_CAMERA_TARGET_OFFSET_STANDING:Vec2D = new Vec2D(300, 0);
		
		//actions
		internal static const MOVE_LEFT		:String = "goLeft";
		internal static const MOVE_RIGHT	:String = "goRight";
		internal static const JUMP			:String = "jump";
		internal static const DASH			:String = "dash";
		
		
		//state
		//------------------------------------------------------------------------------------------------
		
		internal var direction:Number = 1;
		internal var state:PlayerState;
		internal var jumpCount:Number = 0;
		internal var moving:Boolean = false;
		internal var dashing:Boolean = false;
		internal var onFloor:Boolean = true;
		internal var onWall:Boolean = false;
		internal var wallingDirection:Number = 0;
		
		//------------------------------------------------------------------------------------------------
		//end of state
		
		
		internal var renderTarget:PlayerRenderTarget;
		
		public function PlayerController() {
			
		}
		
		//quick UGLY hack
		public function die():void {
			setPlayerMovieClip(renderTarget.die);
		}
		
		
		//on add
		//------------------------------------------------------------------------------------------------
		
		override public function onAdd():void {
			entity.addEventListener(ContactEvent.BEGIN, onContactBegin);
			entity.addEventListener(ContactEvent.END, onContactEnd);
		}
		
		public function resetContactCounter():void {
			contactFloorCount = contactWallCount = 0;
		}
		private var contactFloorCount:int = 0;
		private var contactWallCount:int = 0;
		private function onContactBegin(e:ContactEvent):void {
			switch (e.fixture2.categoryBits) {
				case Box2DCategory.FLOOR:
					engine.logger.log("Contacted floor.", LogType.INFO, this);
					contactFloorCount++;
					onFloor = Boolean(contactFloorCount);
					engine.logger.log("Contacted floor collider count: " + contactFloorCount.toString(), LogType.INFO, this);
					break;
				case Box2DCategory.WALL:
					engine.logger.log("Contacted wall.", LogType.INFO, this);
					contactWallCount++;
					onWall = Boolean(contactWallCount);
					wallingDirection = e.fixture1.userData;
					break;
			}
		}
		
		private function onContactEnd(e:ContactEvent):void {
			switch (e.fixture2.categoryBits) {
				case Box2DCategory.FLOOR:
					engine.logger.log("Left floor.", LogType.INFO, this);
					contactFloorCount--;
					onFloor = Boolean(contactFloorCount);
					engine.logger.log("Contacted floor collider count: " + contactFloorCount.toString(), LogType.INFO, this);
					break;
				case Box2DCategory.WALL:
					engine.logger.log("Left wall.", LogType.INFO, this);
					contactWallCount--;
					onWall = Boolean(contactWallCount);
					wallingDirection = 0;
					break;
			}
		}
		
		public function setPlayerMovieClip(clip:MovieClip):void {
			var container:DisplayObjectContainer = renderTarget.displayObject as DisplayObjectContainer;
			while (container.numChildren) container.removeChildAt(0);
			clip.gotoAndPlay(1);
			container.addChild(clip);
		}
		
		override public function onEntityAdd():void {
			state = new StandingState(this, direction);
			setPlayerMovieClip(renderTarget.standing);
			
			var km:KeyManager = engine.keyManager;
			km.mapKeyToAction(Key.LEFT, MOVE_LEFT);
			km.mapKeyToAction(Key.RIGHT, MOVE_RIGHT);
			km.mapKeyToAction(Key.X, JUMP);
			km.mapKeyToAction(Key.Z, DASH);
			
			km.addEventListener(KeyManagerEvent.ACTION_START, onActionStart);
			km.addEventListener(KeyManagerEvent.ACTION_END, onActionEnd);
		}
		
		private function onActionStart(e:KeyManagerEvent):void {
			if (!active) return;
			
			engine.logger.log("Action started: " + e.action, LogType.INFO, this);
			switch (e.action) {
				case MOVE_LEFT:
					engine.logger.log("Moving left.", LogType.INFO, this);
					direction = -1;
					moving = true;
					break;
				case MOVE_RIGHT:
					engine.logger.log("Moving right.", LogType.INFO, this);
					moving = true;
					direction = 1;
					break;
				case DASH:
					dashing = true;
					break;
				case JUMP:
					state.startJump();
					break;
			}
		}
		
		private function onActionEnd(e:KeyManagerEvent):void {
			if (!active) return;
			
			engine.logger.log("Action ended: " + e.action, LogType.INFO, this);
			var km:KeyManager = engine.keyManager;
			switch (e.action) {
				case MOVE_LEFT:
				case MOVE_RIGHT:
					if ((!km.actionActive(MOVE_LEFT)) && (!km.actionActive(MOVE_RIGHT))) {
						engine.logger.log("Stop moving.", LogType.INFO, this);
						moving = false;
					}
					break;
				case DASH:
					dashing = false;
					break;
				case JUMP:
					state.stopJump();
					break;
			}
		}
		
		//------------------------------------------------------------------------------------------------
		//end of on add
		
		
		//on remove
		//------------------------------------------------------------------------------------------------
		
		override public function onEntityRemove():void {
			//dummy state object (does nothing)
			state = new PlayerState(this, direction);
			
			var km:KeyManager = engine.keyManager;
			km.removeEventListener(KeyManagerEvent.ACTION_START, onActionStart);
			km.removeEventListener(KeyManagerEvent.ACTION_END, onActionEnd);
		}
		
		override public function onRemove():void {
			entity.removeEventListener(ContactEvent.BEGIN, onContactBegin);
			entity.removeEventListener(ContactEvent.END, onActionEnd);
		}
		
		//------------------------------------------------------------------------------------------------
		//end of on remove
		
		override public function onTick(time:Number):void {
			state.onTick(time);
			
			var vel:Vec2D = entity.getProperty(Ref.VELOCITY);
			vel.y = RusherMath.clamp(vel.y, -MAX_FALLING_SPEED, MAX_FALLING_SPEED);
			entity.setProperty(Ref.VELOCITY, vel);
		}
		
		/*
		override public function onTick(time:Number):void {
			var km:KeyManager = engine.keyManager;
			
			var vx:Number = 0;
			var vy:Number = 0;
			
			if (km.actionActive(MOVE_RIGHT)) {
				if (km.actionActive(DASH)) vx += RUNNING_SPEED ;
				else vx += WALKING_SPEED;
			}
			if (km.actionActive(MOVE_LEFT)) {
				if (km.actionActive(DASH)) vx -= RUNNING_SPEED;
				else vx -= WALKING_SPEED;
			}
			
			var vel:Vec2D = entity.getProperty(Ref.VELOCITY);
			vel.x = vx;
			if (vy) vel.y = vy;
			entity.setProperty(Ref.VELOCITY, vel);
			
			var pos:Vec2D = entity.getProperty(Ref.POSITION);
			var frontCameraTargetPos:Vec2D = entity.getProperty(Ref.FRONT_CAMERA_TARGET);
			var backCameraTargetPos:Vec2D = entity.getProperty(Ref.BACK_CAMERA_TARGET);
			if (vx > 0) {
				frontCameraTargetPos = new Vec2D(pos.x + FRONT_CAMERA_TARGET_OFFSET_RUNNING.x, pos.y);
				backCameraTargetPos = new Vec2D(pos.x + BACK_CAMERA_TARGET_OFFSET_RUNNING.x, pos.y);
			} else if (vx < 0) {
				frontCameraTargetPos = new Vec2D(pos.x - FRONT_CAMERA_TARGET_OFFSET_RUNNING.x, pos.y);
				backCameraTargetPos = new Vec2D(pos.x - BACK_CAMERA_TARGET_OFFSET_RUNNING.x, pos.y);
			} else {
				frontCameraTargetPos = new Vec2D(pos.x + FRONT_CAMERA_TARGET_OFFSET_STANDING.x, pos.y);
				backCameraTargetPos = new Vec2D(pos.x - FRONT_CAMERA_TARGET_OFFSET_STANDING.x, pos.y);
			}
			
			entity.setProperty(Ref.FRONT_CAMERA_TARGET, frontCameraTargetPos);
			entity.setProperty(Ref.BACK_CAMERA_TARGET, backCameraTargetPos);
		}
		*/
	}
}